객체 프로퍼티
#
객체 프로퍼티#
프로퍼티의 종류- 데이터 프로퍼티 (data property)
- 접근자 프로퍼티 (accessor property)
- 본질은 함수
- 값을 획득(get) / 설정(set)
- 외부 코드에서는 함수가 아닌 일반적인 프로퍼티처럼 보인다.
둘 중 한 종류에만 속할 수 있다.
#
프로퍼티 특성 설명자 Property DescriptorES5 이후, descriptor 값의 조회/수정이 가능해졌다.
#
프로퍼티 값과 플래그객체 프로퍼티: 값(value) + 플래그(flag) 속성 세 가지
value
프로퍼티 값 정의- type: 자바스크립트에서 허용한 모든 값
- Default:
undefined
writable
true
값을 수정할 수 있다.
enumerable
true
반복문을 사용해 나열 가능
configurable
true
프로퍼티 삭제/플래그 수정 가능
프로퍼티 플래그의 기본값은 모두 true
#
Object.getOwnPropertyDescriptor(obj, propertyName)- 특정 프로퍼티에 대한 정보를 모두 반환
obj
정보를 얻고자 하는 객체propertyName
정보를 얻고자 하는 객체 내 프로퍼티
#
Object.defineProperty(obj, propertyName, descriptor)프로퍼티 플래그 변경
[
obj
,propertyName
] = [설명자를 적용하고 싶은 객체, 객체 프로퍼티]propertyName
이 없으면, 새로운 프로퍼티 생성
[
descriptor
] = [적용하고자 하는 프로퍼티 설명자]플래그 정보가 없으면 default 는
false
#
writable 플래그비엄격 모드
- 읽기 전용 프로퍼티 값 수정 -> 에러 X
- BUT, 값 변경 X
- 플래그에서 정한 규칙을 위반 -> 에러 없이 그냥 무시
#
enumerable 플래그객체 내장 메서드 toString
- non-enumerable
- for...in, Object.keys(), Object.entries(), Object.values() 에서 순회하지 않음.
- custom toString 을 추가하면 for..in 에서 나타남
#
configurable 플래그false
- 해당 프로퍼티는 제거/변경 X
- configurable/enumerable 플래그 수정 X
- writable
- false -> true (X)
- true -> false (O)
- 접근자 프로퍼티 get/set 을 변경 X (새롭게 만드는 것은 가능)
#
Object.defineProperties(obj, descriptors)프로퍼티 여러개를 한 번에 정의할 수 있다.
#
Object.getOwnPropertyDescriptors프로퍼티 설명자를 전부 한꺼번에 가져올 수 있다.
Object.defineProperties
와 함께 사용하면- 플래그, 심볼형 프로퍼티도 함께 복사
#
객체 수정을 막아주는 다양한 메서드Object.preventExtensions(obj)
객체에 새로운 프로퍼티를 추가할 수 없게 합니다.Object.seal(obj)
새로운 프로퍼티 추가나 기존 프로퍼티 삭제를 막아줍니다. 프로퍼티 전체에 configurable: false를 설정하는 것과 동일한 효과입니다.Object.freeze(obj)
새로운 프로퍼티 추가나 기존 프로퍼티 삭제, 수정을 막아줍니다. 프로퍼티 전체에 configurable: false, writable: false를 설정하는 것과 동일한 효과입니다.
제약 사항을 확인하는 메서드
Object.isExtensible(obj)
새로운 프로퍼티를 추가하는 게 불가능한 경우 false를, 그렇지 않은 경우 true를 반환합니다.Object.isSealed(obj)
프로퍼티 추가, 삭제가 불가능하고 모든 프로퍼티가 configurable: false이면 true를 반환합니다.Object.isFrozen(obj)
프로퍼티 추가, 삭제, 변경이 불가능하고 모든 프로퍼티가 configurable: false, writable: false이면 true를 반환합니다.
#
접근자 프로퍼티 설명자 Accessor Descriptor- value, writable 은 무시된다.
get
인수가 없는 함수, 프로퍼티를 읽을 때 동작set
인수가 하나인 함수, 프로퍼티에 값을 쓸 때 호출됨enumerable
configurable
바깥 코드에선 접근자 프로퍼티를 일반 프로퍼티처럼 사용할 수 있다.
프로퍼티에 getter 메서드만 있기 때문에 에러가 발생한다.
setter 추가
fullName 은 가상의 프로퍼티 이다. 읽고 쓸 순 있지만 실존하지 않는다.
#
자바 getters, setters 의 용도- 유효성 검사
- Lazy Loading
- Read 와 Write 권한을 다르게 설정
#
유효성 검사클래스 외부에서 클래스의 private 필드 를 get/set 할때 사용한다.
- 클래스 필드를 실수로 조작하지 못하도록
유효성 검사
를 수행 할 수있는 중심 위치.
#
캡슐화#
동시성과 멀티스레딩 -> 불변객체 생성- 객체의 상태의 불변성을 위해 setter 가 없다.
- 객체를 동시에 실행중인 다른 스레드로 전달하려면, race condition 및 기타 멀티스레딩의 부작용을 피하기 위해 스레드들을 동기화 해야 한다.
- 불변 객체의 경우 객체 상태가 스레드에 의해 변경될까봐 걱정할 필요가 없다.
- 불변 객체 예제
#
getter 획득자 메서드보통, 계산된 값을 반환
#
Syntax- 함수 이름
- 자바스크립트 변수 이름 생성 규칙 (_$0-9, A-Z,a-z) 에 허용 되는 문자열.
- 매개변수가 없다
#
선언프로퍼티 접근을 해도, 정의된 함수가 호출되고 값이 반환된다.
#
삭제#
defineProperty, 객체에 새 프로퍼티로 추가#
계산된 프로퍼티 이름#
Immutable 패턴객체의 생명주기 동안 내부의 상태가 절대 변경되지 않도록 강제하는 방법
- 객체의 프로퍼티 값 할당을 생성자를 통해서만 할 수 있다.
- 필드에 접근하기 위해서는 Getter 메서드를 사용해야 한다.
#
lazy evaluationgetter 프로퍼티에 접근하기 전까지는 값을 계산하지 않는다.
- 값의 계산 비용이 큰 경우
- RAM 이나 CPU 의 시간을 많이 소모할 때
- worker thread 생성 (?)
- 원격 파일 로드
- 값이 당장 필요하지 않을때, 나중에 이용될 때, 절대로 이용되지 않을 때
- 값이 다시 계산되어서는 안되는 경우.
- 값이 여러차례 이용되지만, 절대 변경되지 않아 매번 다시 계산할 필요가 없을 때
각 케이스가 궁금하다 TODO
#
getter 반환값의 캐싱getter 은 첫 호출 이후에는 다시 계산하지 않고 이 캐시 값을 반환한다.
setter 에서 delete 를 하든 getter 에서 delete 를 하든, delete 를 호출하면 setter, getter 모두가 해제 된다.
o.foo = "test"
실행 -> getter 인 foo 삭제console.log(o.foo)
실행get foo() {}
를 실행 Xo.foo
프로퍼티의 값을 반환하여 "test" 를 출력 O
🔗
o.foo = "test"
이후의 setter 가 활성화 되고, delete this.foo
는 setter 를 삭제하여
다음줄의 this.foo
는 객체의 프로퍼티에 값을 할당하게 됩니다.
그러나 만약에 delete this.foo
가 없다면, this.foo
는 다시 setter 를 호출하게 되므로 throws RangeError maximum call
를 유발합니다. 이것의 대안은 this._foo
처럼 프로퍼티 명을 private 형식으로 지정해 줄 수 있습니다.
get foo() { delete this.foo }
foo 프로퍼티를 삭제해도 결과는 test
을 출력한다. 왜 delete 를 적어준 것일까요?
- 다시 o.foo 를 얻어 올때, getter 으로 값을 다시 계산하는 것을 없애기 위해서 입니다. 단순히 o의 프로퍼티인 foo 로서 값을 얻어 옵니다.
return this._foo = 'something';
문자열을 반환하는 이유이 예제는 getter 와 setter 를 캐싱으로서 사용하지 않을 때 입니다.
아무리 setter 를 해서 _foo 의 값을 변경 시켜도, o.foo
으로 getter 를 호출해도 항상 'something' 으로 됩니다. this._foo = 'something'
에서 다시 setter 를 호출하기 때문입니다.
이 예제는 도대체 왜 만든건지 모르겠네요.
중요한 것은, 캐싱을 위해 의도적으로 getter 와 setter 를 사용할 수 있다는 점 같습니다.
#
setter 설정자 메서드#
setter 에서 값을 할당하는 내부적 과정#
1. 접근하는 프로퍼티 탐색직속 프로퍼티
접근하는 프로퍼티가 Accessor Descriptor(Getter/Setter) 일 때
- [Setter] 호출.
- [Setter] 가 없고 [Getter]만 있을 때
- 값 할당은 무시된다.
접근자 설명자가 아니고,
writable: false
일 때- 조용히 실패
- 엄격모드일 땐, Type Error 발생
1번 2번 모두 해당하지 않을때, 프로퍼티에 값을 세팅
접근하는 프로퍼티가 없을 때
- 객체와 연결된 상위 [[Prototype]] 체인을 순회한다.
#
2. 모든 [[Prototype]] 체인에서 프로퍼티가 발견되지 않을 경우- Object extensible
- 직속 프로퍼티(Directly Present) 생성하고 값을 할당
- not Object extensible
- Object.preventExtensions(), Object.isExtensible()[false] 일 때, childCat 을 확장 불가능하게 만듭니다
TypeError
발생- 프로퍼티의 삭제/수정 가능
- 오브젝트의 proto 에 값 추가, 변경 가능
[[Prototype]] 에 새 프로퍼티를 추가합니다
- Object.preventExtensions(), Object.isExtensible()[false] 일 때,
🔗#
3. 상위 [[Prototype]] 체인에서 프로퍼티가 발견된 경우writable: true
- 직속 프로퍼티를 생성하고 값을 할당
- 의도는 상위 수준의 프로퍼티에 할당을 하려고 했지만 직속 프로퍼티가 추가 된다.
- 객체지향에서 Overriding 이라고 부르는 Shadowing 이 발생한다.
이것이 위임을 통한 프로퍼티 가려짐이다. anotherObj.a가 증가함이 아니라. myObject에 a가 새로 할당된다.
- 직속 프로퍼티를 생성하고 값을 할당
writable: false
- 아무일도 일어나지 않는다.
- 엄격모드일 경우 TypeError
프로퍼티가 [Setter] 일 경우, 항상 Setter 가 호출된다.
- 직속 프로퍼티가 추가 되지 않는다.
- Setter 를 덮어 쓰려면
Object.defineProperty
를 사용해야 한다.
#
getter 와 setter 똑똑하게 활용하기#
1. 실제 프로퍼티 값을 감싸는 wrapper 처럼 사용프로퍼티 값을 원하는 대로 통제 가능
user.name 으로 접근할 수 있지만. 밑줄로 시작하는 프로퍼티는 객체 내부에서만 활용하고, 외부에서는 건드리지 않는 것이 관습.
#
2. 호환성을 위해 사용하기데이터 프로퍼티 name, age 를 사용해서 사용자를 나타내는 객체 구현
요구사항이 바뀌어 age 를 birthday 로 저장해야 할 경우. (birthday 가 더 정확하고 편리하기 때문)
기존 코드에 age 를 사용하고 있는 코드를 모두 찾아서 수정해야 하는 문제점이 있다.
#
Property - Existence객체에 프로퍼티가 존재하는지 여부를 확인하는 방법
in
연산자- [[Prototype]] 체인을 모두 순회하며 키 존재 여부를 검사한다.
Object.hasOwnProperty
- 직속 프로퍼티 존재 여부만을 검사한다.
#
Object.seal- 더 이상 확장 불가능 하게 만듦(프로퍼티 추가가 불가능한 상태)
- Object.defineProperty 로 Descriptor 를 변경하는 행위를 막음
- 프로퍼티 삭제 불가
#
Object.freeze- Object.seal 의 모든 동작을 포함
- 프로퍼티 값 또한 변경 불가능 하게 만든다.
- 중첩된 접근 값이 변경은 가능하다.
obj.a = { b: 1 }
이라면obj.a
는 변경 불가 하지만,obj.a.b
는 변경 가능합니다.